@@ -0,0 +1,30 @@ |
||
| 1 |
+.agent-diagram {
|
|
| 2 |
+ position: relative; |
|
| 3 |
+ z-index: auto; |
|
| 4 |
+ |
|
| 5 |
+ svg.diagram {
|
|
| 6 |
+ position: absolute; |
|
| 7 |
+ z-index: 1; |
|
| 8 |
+ } |
|
| 9 |
+ |
|
| 10 |
+ .overlay-container {
|
|
| 11 |
+ position: absolute; |
|
| 12 |
+ top: 0; |
|
| 13 |
+ left: 0; |
|
| 14 |
+ z-index: auto; |
|
| 15 |
+ |
|
| 16 |
+ .overlay {
|
|
| 17 |
+ position: relative; |
|
| 18 |
+ z-index: auto; |
|
| 19 |
+ width: 100%; |
|
| 20 |
+ height: 100%; |
|
| 21 |
+ |
|
| 22 |
+ .badge {
|
|
| 23 |
+ position: absolute; |
|
| 24 |
+ display: none; |
|
| 25 |
+ color: white !important; |
|
| 26 |
+ z-index: 2; |
|
| 27 |
+ } |
|
| 28 |
+ } |
|
| 29 |
+ } |
|
| 30 |
+} |
@@ -6,7 +6,7 @@ module DotHelper |
||
| 6 | 6 |
dot.close_write |
| 7 | 7 |
dot.read |
| 8 | 8 |
} rescue false) |
| 9 |
- svg.html_safe |
|
| 9 |
+ decorate_svg(svg, agents).html_safe |
|
| 10 | 10 |
else |
| 11 | 11 |
tag('img', src: URI('https://chart.googleapis.com/chart').tap { |uri|
|
| 12 | 12 |
uri.query = URI.encode_www_form(cht: 'gv', chl: agents_dot(agents)) |
@@ -161,4 +161,85 @@ module DotHelper |
||
| 161 | 161 |
} |
| 162 | 162 |
} |
| 163 | 163 |
end |
| 164 |
+ |
|
| 165 |
+ def decorate_svg(xml, agents) |
|
| 166 |
+ svg = Nokogiri::XML(xml).at('svg')
|
|
| 167 |
+ |
|
| 168 |
+ Nokogiri::HTML::Document.new.tap { |doc|
|
|
| 169 |
+ doc << root = Nokogiri::XML::Node.new('div', doc) { |div|
|
|
| 170 |
+ div['class'] = 'agent-diagram' |
|
| 171 |
+ } |
|
| 172 |
+ |
|
| 173 |
+ svg['class'] = 'diagram' |
|
| 174 |
+ |
|
| 175 |
+ root << svg |
|
| 176 |
+ root << overlay_container = Nokogiri::XML::Node.new('div', doc) { |div|
|
|
| 177 |
+ div['class'] = 'overlay-container' |
|
| 178 |
+ div['style'] = "width: #{svg['width']}; height: #{svg['height']}"
|
|
| 179 |
+ } |
|
| 180 |
+ overlay_container << overlay = Nokogiri::XML::Node.new('div', doc) { |div|
|
|
| 181 |
+ div['class'] = 'overlay' |
|
| 182 |
+ } |
|
| 183 |
+ |
|
| 184 |
+ svg.xpath('//xmlns:g[@class="node"]', svg.namespaces).each { |node|
|
|
| 185 |
+ agent_id = (node.xpath('./xmlns:title/text()', svg.namespaces).to_s[/\d+/] or next).to_i
|
|
| 186 |
+ agent = agents.find { |a| a.id == agent_id }
|
|
| 187 |
+ |
|
| 188 |
+ count = agent.events_count |
|
| 189 |
+ next unless count && count > 0 |
|
| 190 |
+ |
|
| 191 |
+ overlay << Nokogiri::XML::Node.new('a', doc) { |badge|
|
|
| 192 |
+ badge['id'] = id = 'b%d' % agent_id |
|
| 193 |
+ badge['class'] = 'badge' |
|
| 194 |
+ badge['href'] = events_path(agent: agent) |
|
| 195 |
+ badge['target'] = '_blank' |
|
| 196 |
+ badge.content = count.to_s |
|
| 197 |
+ |
|
| 198 |
+ node['data-badge-id'] = id |
|
| 199 |
+ |
|
| 200 |
+ badge << Nokogiri::XML::Node.new('span', doc) { |label|
|
|
| 201 |
+ # a dummy label only to obtain the background color |
|
| 202 |
+ label['class'] = [ |
|
| 203 |
+ 'label', |
|
| 204 |
+ if agent.disabled? |
|
| 205 |
+ 'label-warning' |
|
| 206 |
+ elsif agent.working? |
|
| 207 |
+ 'label-success' |
|
| 208 |
+ else |
|
| 209 |
+ 'label-danger' |
|
| 210 |
+ end |
|
| 211 |
+ ].join(' ')
|
|
| 212 |
+ label['style'] = 'display: none'; |
|
| 213 |
+ } |
|
| 214 |
+ } |
|
| 215 |
+ } |
|
| 216 |
+ |
|
| 217 |
+ root << Nokogiri::XML::Node.new('script', doc) { |script|
|
|
| 218 |
+ script.content = <<-SCRIPT |
|
| 219 |
+$(function () {
|
|
| 220 |
+ var svg = document.querySelector('.agent-diagram svg.diagram');
|
|
| 221 |
+ var overlay = document.querySelector('.agent-diagram .overlay');
|
|
| 222 |
+ var getTopLeft = function (node) {
|
|
| 223 |
+ var bbox = node.getBBox(); |
|
| 224 |
+ var point = svg.createSVGPoint(); |
|
| 225 |
+ point.x = bbox.x + bbox.width; |
|
| 226 |
+ point.y = bbox.y; |
|
| 227 |
+ return point.matrixTransform(node.getCTM()); |
|
| 228 |
+ }; |
|
| 229 |
+ $(svg).find('g.node[data-badge-id]').each(function () {
|
|
| 230 |
+ var tl = getTopLeft(this) |
|
| 231 |
+ $('#' + this.getAttribute('data-badge-id'), overlay).each(function () {
|
|
| 232 |
+ var badge = $(this); |
|
| 233 |
+ badge.css({
|
|
| 234 |
+ left: tl.x - badge.outerWidth() * (2/3), |
|
| 235 |
+ top: tl.y - badge.outerHeight() * (1/3), |
|
| 236 |
+ 'background-color': badge.find('.label').css('background-color')
|
|
| 237 |
+ }).show(); |
|
| 238 |
+ }); |
|
| 239 |
+ }); |
|
| 240 |
+}) |
|
| 241 |
+ SCRIPT |
|
| 242 |
+ } |
|
| 243 |
+ }.at('div.agent-diagram').to_s
|
|
| 244 |
+ end |
|
| 164 | 245 |
end |